home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Java Primer Plus
/
Java Primer Plus (Waite Group Proess)(1996).iso
/
java_Win
/
demo
/
Fractal
/
CLSFractal.java
< prev
next >
Wrap
Text File
|
1995-10-12
|
8KB
|
358 lines
import java.awt.Graphics;
import java.util.Stack;
import java.util.Vector;
/**
* A (not-yet) Context sensitive L-System Fractal applet class.
*
* The rules for the Context L-system are read from the java.applet.Applet's
* attributes and then the system is iteratively applied for the
* given number of levels, possibly drawing each generation as it
* is generated. Note that the ContextLSystem class does not yet
* handle the lContext and rContext attributes, although this
* class is already designed to parse the '[' and ']' characters
* typically used in Context sensitive L-Systems.
*
* @author Jim Graham
* @version 1.1f, 27 Mar 1995
*/
public class CLSFractal extends java.applet.Applet implements Runnable {
ContextLSystem cls;
int fractLevel = 1;
int repaintDelay = 50;
boolean incrementalUpdates;
float startAngle;
float rotAngle;
float Xmin;
float Xmax;
float Ymin;
float Ymax;
int border;
boolean normalizescaling;
public void init() {
String s;
cls = new ContextLSystem(this);
s = getParameter("level");
if (s != null) fractLevel = Integer.parseInt(s);
s = getParameter("incremental");
if (s != null) incrementalUpdates = s.equals("true");
s = getParameter("delay");
if (s != null) repaintDelay = Integer.parseInt(s);
s = getParameter("startAngle");
if (s != null) startAngle = Float.valueOf(s).floatValue();
s = getParameter("rotAngle");
if (s != null) rotAngle = Float.valueOf(s).floatValue();
rotAngle = rotAngle / 360 * 2 * 3.14159265358f;
s = getParameter("border");
if (s != null) border = Integer.parseInt(s);
s = getParameter("normalizescale");
if (s != null) normalizescaling = s.equals("true");
}
Thread kicker;
public void run() {
Thread me = Thread.currentThread();
boolean needsRepaint = false;
while (kicker == me && cls.getLevel() < fractLevel) {
cls.generate();
if (kicker == me && incrementalUpdates) {
repaint();
try {Thread.sleep(repaintDelay);} catch (InterruptedException e){}
} else {
needsRepaint = true;
}
}
if (kicker == me) {
kicker = null;
if (needsRepaint) {
repaint();
}
}
}
public void start() {
kicker = new Thread(this);
kicker.start();
}
public void stop() {
kicker = null;
}
public boolean mouseUp(java.awt.Event evt, int x, int y) {
cls = new ContextLSystem(this);
savedPath = null;
start();
return true;
}
String savedPath;
public void paint(Graphics g) {
String fractalPath = cls.getPath();
if (fractalPath == null) {
super.paint(g);
return;
}
if (savedPath == null || !savedPath.equals(fractalPath)) {
savedPath = fractalPath;
render(null, fractalPath);
}
for (int i = 0; i < border; i++) {
g.draw3DRect(i, i, size().width - i * 2, size().height - i * 2,false);
}
render(g, fractalPath);
}
void render(Graphics g, String path) {
Stack turtleStack = new Stack();
CLSTurtle turtle;
if (g == null) {
Xmin = 1E20f;
Ymin = 1E20f;
Xmax = -1E20f;
Ymax = -1E20f;
turtle = new CLSTurtle(startAngle, 0, 0, 0, 0, 1, 1);
} else {
float frwidth = Xmax - Xmin;
if (frwidth == 0)
frwidth = 1;
float frheight = Ymax - Ymin;
if (frheight == 0)
frheight = 1;
float xscale = (size().width - border * 2 - 1) / frwidth;
float yscale = (size().height - border * 2 - 1) / frheight;
int xoff = border;
int yoff = border;
if (normalizescaling) {
if (xscale < yscale) {
yoff += ((size().height - border * 2)
- ((Ymax - Ymin) * xscale)) / 2;
yscale = xscale;
} else if (yscale < xscale) {
xoff += ((size().width - border * 2)
- ((Xmax - Xmin) * yscale)) / 2;
xscale = yscale;
}
}
turtle = new CLSTurtle(startAngle, 0 - Xmin, 0 - Ymin,
xoff, yoff, xscale, yscale);
}
for (int pos = 0; pos < path.length(); pos++) {
switch (path.charAt(pos)) {
case '+':
turtle.rotate(rotAngle);
break;
case '-':
turtle.rotate(-rotAngle);
break;
case '[':
turtleStack.push(turtle);
turtle = new CLSTurtle(turtle);
break;
case ']':
turtle = (CLSTurtle) turtleStack.pop();
break;
case 'f':
turtle.jump();
break;
case 'F':
if (g == null) {
includePt(turtle.X, turtle.Y);
turtle.jump();
includePt(turtle.X, turtle.Y);
} else {
turtle.draw(g);
}
break;
default:
break;
}
}
}
void includePt(float x, float y) {
if (x < Xmin)
Xmin = x;
if (x > Xmax)
Xmax = x;
if (y < Ymin)
Ymin = y;
if (y > Ymax)
Ymax = y;
}
}
/**
* A Logo turtle class designed to support Context sensitive L-Systems.
*
* This turtle performs a few basic maneuvers needed to support the
* set of characters used in Context sensitive L-Systems "+-fF[]".
*
* @author Jim Graham
* @version 1.1f, 27 Mar 1995
*/
class CLSTurtle {
float angle;
float X;
float Y;
float scaleX;
float scaleY;
int xoff;
int yoff;
public CLSTurtle(float ang, float x, float y,
int xorg, int yorg, float sx, float sy) {
angle = ang;
scaleX = sx;
scaleY = sy;
X = x * sx;
Y = y * sy;
xoff = xorg;
yoff = yorg;
}
public CLSTurtle(CLSTurtle turtle) {
angle = turtle.angle;
X = turtle.X;
Y = turtle.Y;
scaleX = turtle.scaleX;
scaleY = turtle.scaleY;
xoff = turtle.xoff;
yoff = turtle.yoff;
}
public void rotate(float theta) {
angle += theta;
}
public void jump() {
X += (float) Math.cos(angle) * scaleX;
Y += (float) Math.sin(angle) * scaleY;
}
public void draw(Graphics g) {
float x = X + (float) Math.cos(angle) * scaleX;
float y = Y + (float) Math.sin(angle) * scaleY;
g.drawLine((int) X + xoff, (int) Y + yoff,
(int) x + xoff, (int) y + yoff);
X = x;
Y = y;
}
}
/**
* A (non-)Context sensitive L-System class.
*
* This class initializes the rules for Context sensitive L-Systems
* (pred, succ, lContext, rContext) from the given java.applet.Applet's attributes.
* The generate() method, however, does not (yet) apply the lContext
* and rContext parts of the rules.
*
* @author Jim Graham
* @version 1.1f, 27 Mar 1995
*/
class ContextLSystem {
String axiom;
Vector rules = new Vector();
int level;
public ContextLSystem(java.applet.Applet app) {
axiom = app.getParameter("axiom");
int num = 1;
while (true) {
String pred = app.getParameter("pred"+num);
String succ = app.getParameter("succ"+num);
if (pred == null || succ == null) {
break;
}
rules.addElement(new CLSRule(pred, succ,
app.getParameter("lContext"+num),
app.getParameter("rContext"+num)));
num++;
}
currentPath = new StringBuffer(axiom);
level = 0;
}
public int getLevel() {
return level;
}
StringBuffer currentPath;
public synchronized String getPath() {
return ((currentPath == null) ? null : currentPath.toString());
}
private synchronized void setPath(StringBuffer path) {
currentPath = path;
level++;
}
public void generate() {
StringBuffer newPath = new StringBuffer();
int pos = 0;
while (pos < currentPath.length()) {
CLSRule rule = findRule(pos);
if (rule == null) {
newPath.append(currentPath.charAt(pos));
pos++;
} else {
newPath.append(rule.succ);
pos += rule.pred.length();
}
}
setPath(newPath);
}
public CLSRule findRule(int pos) {
for (int i = 0; i < rules.size(); i++) {
CLSRule rule = (CLSRule) rules.elementAt(i);
if (rule.matches(currentPath, pos)) {
return rule;
}
}
return null;
}
}
/**
* A Context sensitive L-System production rule.
*
* This class encapsulates a production rule for a Context sensitive
* L-System (pred, succ, lContext, rContext).
* The matches() method, however, does not (yet) verify the lContext
* and rContext parts of the rule.
*
* @author Jim Graham
* @version 1.1f, 27 Mar 1995
*/
class CLSRule {
String pred;
String succ;
String lContext;
String rContext;
public CLSRule(String p, String d, String l, String r) {
pred = p;
succ = d;
lContext = l;
rContext = r;
}
public boolean matches(StringBuffer sb, int pos) {
if (pos + pred.length() > sb.length()) {
return false;
}
char cb[] = new char[pred.length()];
sb.getChars(pos, pos + pred.length(), cb, 0);
return pred.equals(new String(cb));
}
}